﻿
from strategy_platform.api import (sub_realmd,unsub_realmd,register_realmd_cb)
from strategy_platform.api import (sub_order, unsub_order, submit_order, cancel_order, register_order_cb, unregister_order_cb, unsub_realmd, unregister_realmd_cb)
from strategy_platform.api import (submit_order,query_order,cancel_order, query_position,query_commodity_info,query_credit_fund_info)
from strategy_platform.api import (add_argument)
from strategy_platform.api import (second_timer, minute_timer, at_day_timer)
from datetime import datetime, timedelta, date
import time, pandas   
import threading  

# need to checkl
def get_dir_(order):
	if order.side == "1":
		return 1
	elif order.side == '2':
		return -1
	else:
		log.error("UNEXPECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
		return 0

# need to checkl
def get_filledqty_(order): #can be positive or negative
	assert((order.qty>0) and (order.filledQty>0)) #to be checked
	return order.filledQty * get_dir_(order)

def send_order(code, qty, price): #qty could be pos or neg, amount must be positive
	
	log.info('placing order to get %d at %7.3f'%(qty, price))
	if qty>0:
		submit_order(g['acct_type'], g['acct'], code, "1", 0, price, qty)	
	elif qty<0:
		submit_order(g['acct_type'], g['acct'], code, "2", 0, price, -qty)	

#-----------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------
def on_realmd(realmk_obj, cb_arg):	
	global g
	code    = realmk_obj.symbol
	currbid = realmk_obj.bidPrice1
	currask = realmk_obj.askPrice1
	bsize = realmk_obj.bidVolume1
	asize = realmk_obj.askVolume1
	oo_lock.acquire() 
	if (currbid != g['bid'][code]):
		g['lastbid'][code] = g['bid'][code]
		g['bid'][code]     = currbid
	if (currask != g['ask'][code]):
		g['lastask'][code] = g['ask'][code]
		g['ask'][code]     = currask
	g['bsize'][code]       = bsize
	g['asize'][code]       = asize
	g['ABS'][code]         = 0.95*g['ABS'][code]+0.05*(asize+bsize)/2.0
	g['fv'][code]          = (currbid*asize+currask*bsize) / (asize+bsize+0.0)
	g['high_limit'][code]  = realmk_obj.upperLimit
	g['low_limit'][code]   = realmk_obj.lowerLimit
	oo_lock.release()
	log.info('[%d] %7.3f %7.3f [%d]'%(g['bsize'][code], g['bid'][code], g['ask'][code], g['asize'][code]))


def on_order_update(order_obj, cb_arg): 
	global open_order_dict, stupid_bc_order_dict, stupid_qc_order_dict
	
	log.info(order_obj)
	oid = order_obj.orderNo
	oo_lock.acquire()  
	
	if order_obj.status == 0 or order_obj.status == 1:   # 新单 或 部分成交 
		open_order_dict[oid] = order_obj
	elif order_obj.status == 2 or order_obj.status == 3 or order_obj.status == 4:  # 全成, 部分撤单, 全撤  
		if open_order_dict.has_key(oid):            
			del open_order_dict[oid]
	else:
		log.info('----unknown order status')
		log.info(order_obj)
			
	if order_obj.status == 1: #部分成交 
		stupid_bc_order_dict[oid] = order_obj
	
	if order_obj.status == 2: #全部成交 
		stupid_qc_order_dict[oid] = order_obj

	oo_lock.release()
	
def on_fini():
	unsub_order()   
	unregister_order_cb()
	unsub_realmd(g['security_list'])   
	unregister_realmd_cb() 

	
def on_init(argument_dict):
	global g
	g = {}
	g['acct_type']   = "C"
	g['acct']        = "8009180103"
	g['trade_date']  = int(date.today().strftime('%Y%m%d'))
	for k, v in argument_dict.items():
		g[k] = v
	log.info(g)

	g['task'] = pandas.DataFrame({'code': ['000001.SZ'], 'target': [1000]}) #  #pandas.read_csv('D:/prod/s001/target_trade/intra.h5')
	g['task']['filledqty'] = 0
	g['task'] = g['task'].set_index('code')
	####         target    filledqty
	# 00001.SZ    100			0
	# 00002.SZ   -800         0
	
	lst          = list(g['task'].index)
	g['lastbid'] = {k: 0 for k in lst}
	g['lastask'] = {k: 0 for k in lst}
	g['bid']     = {k: 0 for k in lst}
	g['ask']     = {k: 0 for k in lst}
	g['ABS']     = {k: 0 for k in lst}
	g['fv']      = {k: 0 for k in lst}
	g['bsize']   = {k: 0 for k in lst}
	g['asize']   = {k: 0 for k in lst} 	
	g['security_list'] = lst
	
	register_realmd_cb(on_realmd, None) 
	sub_realmd(lst)
	register_order_cb(on_order_update, None) 
	sub_order() #订阅该策略产生的所有订单
	


def calc_orderqty_(code, qty_left):
	side = 1 if (qty_left>0) else -1
	qty  = qty_left * side 
	if qty < 200:
		return qty * side
	qty = min(qty, 0.1*g['ABS'][code])
	qty = 100 * round(qty/100)
	qty = max(qty, 200)
	return qty*side 

def handle_data(*args, **kwargs):
	global g, stupid_qc_order_dict, stupid_bc_order_dict  
	
	buyorders  = []
	sellorders = []
	for oid, order in open_order_dict.items():
		code = order.symbol
		dir  = get_dir_(order)
		qty       = order.qty * dir 
		filledqty = get_filledqty_(order)
		if dir == 1: #buy order
			buyorders.append({'oid': oid, 'code': code, 'orderprice': order.price, 'orderqty': qty, 'filledqty': filledqty})
		elif dir == -1: #sell order
			sellorders.append({'oid': oid, 'code': code, 'orderprice': order.price, 'orderqty': qty, 'filledqty': filledqty})
	g['buyorders']  = pandas.DataFrame(buyorders)
	if len(g['buyorders'])>0:
		g['buyorders']  = g['buyorders'].sort_values(by=['code', 'orderprice'], ascending=False) # oid, code, orderprice, orderqty, filledqty, sorted by orderprice from high to low
	g['sellorders'] = pandas.DataFrame(sellorders)
	if len(g['sellorders'])>0:
		g['sellorders'] = g['sellorders'].sort_values(by=['code', 'orderprice'], ascending=True)
	
	
	#cats stupid 回传机制， so I can do nothing but this stupid step to calculate my position
	filled_thisp = {k: 0 for k in g['security_list']}
	oo_lock.acquire()
	for oid, order in stupid_qc_order_dict.items():
		code = order.symbol
		filled_thisp[code] += get_filledqty_(order)
		if stupid_bc_order_dict.has_key(oid):            
			del stupid_bc_order_dict[oid]
	stupid_qc_order_dict = {}
	oo_lock.release()
	

	for oid, order in stupid_bc_order_dict.items():
		code = order.symbol
		filled_thisp[code] += get_filledqty_(order)
	g['task'].filledqty = g['task'].filledqty + pandas.Series(filled_thisp)
	#log.info(g['task'])
	
	
	for row in g['task'].itertuples(): #[code] target, filledqty
		code     = row.index
		qty_left = row.target - row.filledqty 
		qty      = calc_orderqty_(qty_left)
		if (qty > 0): # try to buy
			if (g['bid'][code] > g['lastbid'][code]) & (g['bsize'][code] < 99.3*g['ABS'][code]): try2buy(code, qty, g['bid'][code], qty_left)
		elif (qty<0):
			if (g['ask'][code] < g['lastask'][code]) & (g['asize'][code] < 99.3*g['ABS'][code]): try2sell(code, -qty, g['ask'][code], -qty_left)
			
def orderexists_(code, price, dir):
	if dir==1:
		orders = g['buyorders']
	elif dir==-1:
		orders = g['sellorders']
	if len(orders)==0:
		return False
	else:
		return ((orders.code==code) & (orders.orderprice == price)).sum() > 0

def try2buy(code, amount, price, max_outstanding_amount):
	if orderexists_(code, price, 1): return 
	
	existing_orders = g['buyorders']
	existing_orders = existing_orders[existing_orders.code == code].copy()
	if len(existing_orders)>0:
		existing_orders['need2cancel'] = (existing_orders['orderqty'] - existing_orders['filledqty']).cumsum() + amount > max_outstanding_amount
		if existing_orders['need2cancel'].sum() > 0:
			for row in existing_orders[existing_orders['need2cancel']].itertuples():
				cancel_order(g['acct_type'], g['acct'], row.oid)
		else:
			send_order_(code, amount, price)
	else:
		send_order_(code, amount, price)
		

def try2sell(code, amount, price): # to do
	return

oo_lock = threading.Lock()  
open_order_dict    = {}
stupid_bc_order_dict  = {}
stupid_qc_order_dict  = {}
minute_timer(0.1, handle_data)
